/*
 * SIT_PushButton.c : push/toggle button, checkbox and radiobutton.
 *
 * Written by T.Pierron, July 30, 2006.
 */

#include "SIT_P.h"
#include "graphics.h"
#include <stdio.h>

	TagList ButtonClass[] = {
		{ SIT_SuperClass, NULL,         ___, SIT_INT, (ULONG) WidgetClass },
		{ SIT_ButtonType, "buttonType", __G, SIT_INT, OFFSET(SIT_PushButton, sb_Type) },
		{ SIT_CheckState, "checkState", _SG, SIT_INT, OFFSET(SIT_PushButton, sb_State) },
		{ SIT_RadioGroup, "radioGroup", _SG, SIT_INT, OFFSET(SIT_PushButton, sb_Group) },
		{ SIT_IconPath,   "iconPath",   CSG, SIT_PTR, OFFSET(SIT_PushButton, sb_IconPath) },
		{ SIT_IconImage,  "iconImage",  CSG, SIT_PTR, OFFSET(SIT_PushButton, sb_IconImage) },
		{ SIT_IconMargin, "iconMargin", CSG, SIT_INT, OFFSET(SIT_PushButton, sb_IconMargin) },
		{ SIT_IconOrient, "iconOrient", CSG, SIT_INT, OFFSET(SIT_PushButton, sb_IconOrient) },
		{ SIT_AlignHoriz, "alignHoriz", _SG, SIT_INT, OFFSET(SIT_PushButton, sb_AlignText) },
		{ SIT_CurValue,   "curValue",   _SG, SIT_PTR, OFFSET(SIT_PushButton, sb_CurValue) },
		{ SIT_RadioID,    "radioID",    _SG, SIT_INT, OFFSET(SIT_PushButton, sb_RadioID) },
		{ SIT_TagEnd }
	};

static int SIT_MeasureButton(SIT_Widget w, APTR cd, APTR unused2)
{
	static uint8_t check[] = {1, 1, 2, 2, 3};
	#define	btn   ((SIT_PushButton)w)
	HDC     hdc = GetDC(w->sw_Handle);
	HGDIOBJ old = SelectObject(hdc, w->sw_Font);
	LPWSTR  str = w->sw_Name;
	SIZE *  ret = cd;

	if (btn->sb_Icon == NULL || btn->sb_IconOrient != SITV_IconOrientCenter)
	{
		LPWSTR p;
		if (w->sw_Title)
			UTF8ToUTF16(w->sw_Title, str);

		for (p = str; *p; )
			if (p[0] == '&' && p[1]) wcscpy(p, p + 1);
			else p ++;

		if (*str)
			GetTextExtentPoint32(hdc, str, wcslen(str), ret);
		else
			GetTextExtentPoint32(hdc, L" ", 1, ret);
		ret->cx += *str ? 10 : 6;
	}
	else ret->cx = 10, ret->cy = 4;

	switch (btn->sb_Type) {
	case SITV_RadioButton:
	case SITV_CheckBox:
	case SITV_3StateCB: ret->cx += 6; break;
	default:
		ret->cy = ret->cy * 7 / 4;
	}

	if (check[btn->sb_IconOrient] & 1) ret->cx += btn->sb_IconSize.cx;
	else if (ret->cx < btn->sb_IconSize.cx + 6) ret->cx = btn->sb_IconSize.cx + 6;

	if (check[btn->sb_IconOrient] & 2) ret->cy += btn->sb_IconSize.cy;
	else if (ret->cy < btn->sb_IconSize.cy + 6) ret->cy = btn->sb_IconSize.cy + 6;

	ret->cy += w->sw_Margins[1] + w->sw_Margins[3];
	ret->cx += w->sw_Margins[0] + w->sw_Margins[2];

	SelectObject(hdc, old);
	ReleaseDC(w->sw_Handle, hdc);

	return 0;
	#undef	btn
}

/* Be sure there is only one radio button checked in the group */
void SIT_UncheckRadio(SIT_Widget w)
{
	SIT_Widget list;

	for (list = HEAD(w->sw_Parent->sw_Children); list; NEXT(list))
	{
		SIT_PushButton pb = (SIT_PushButton) list;

		if (list != w && list->sw_Type == SIT_PUSHBUTTON && pb->sb_Type == SITV_RadioButton &&
		    pb->sb_Group == ((SIT_PushButton)w)->sb_Group && pb->sb_State)
		{
			SendMessage(list->sw_Handle, BM_SETCHECK, pb->sb_State = SITV_Unchecked, 0);
			SIT_ApplyCallback(list, 0, SITE_OnChanged);
			break;
		}
	}
}

/* Check or radio button activated: sync value with user pointer */
void SIT_SyncCBValue(SIT_PushButton button)
{
	switch (button->sb_Type) {
	case SITV_CheckBox:
	case SITV_3StateCB:
		* button->sb_CurValue = button->sb_State;
		break;
	case SITV_RadioButton:
		* button->sb_CurValue = button->sb_RadioID;
	}
}

#define	SITF_Style3Changed         0x80000000

static int SIT_SetPushButtonStyles(SIT_Widget w, APTR call_data, APTR user_data)
{
	SIT_PushButton button = (APTR) w;
	if (w->sw_Flags & SITF_Style3Changed)
	{
		int val = * button->sb_CurValue;
		switch (button->sb_Type) {
		case SITV_CheckBox:
		case SITV_3StateCB:
			if (button->sb_State != val)
			{
				button->sb_State = val;
				w->sw_Flags |= SITF_Style1Changed;
			}
			break;
		case SITV_RadioButton:
			val = val == button->sb_RadioID;
			if (val != (button->sb_State > 0))
			{
				button->sb_State = val;
				w->sw_Flags |= SITF_Style1Changed;
			}
		}
		w->sw_Flags &= ~SITF_Style3Changed;
	}
	if (w->sw_Flags & SITF_Style1Changed)
	{
		SendMessage(w->sw_Handle, BM_SETCHECK, button->sb_State, 0);

		if (button->sb_State)
		{
			if (button->sb_Type == SITV_RadioButton) SIT_UncheckRadio(w);
			if (button->sb_CurValue) SIT_SyncCBValue(button);
		}
		if (HAS_EVT(w, SITE_OnChanged))
			SIT_ApplyCallback(w, (APTR) button->sb_State, SITE_OnChanged);

		w->sw_Flags &= ~SITF_Style1Changed;
	}
	if (w->sw_Flags & SITF_Style2Changed)
	{
		static uint8_t dir[] = {2, 0, 3, 1, 0, 0, 2, 1, 3, 0};
		Image img = button->sb_IconImage;
		Bool dtor = False;

		if (img == NULL && button->sb_IconPath)
			img = GFX_LoadImage(button->sb_IconPath), dtor = True;
		w->sw_Flags &= ~SITF_Style2Changed;
		button->sb_IconPath = NULL;

		if (img)
		{
			if (button->sb_Icon) pImageList_Destroy(button->sb_Icon);
			button->sb_IconImage = NULL;

			BUTTON_IMAGELIST il = {};
//			if (! has_syslink && img->bpp == 32) GFX_FlattenImage(img, SYSCOL(GFX_ColorBgButton));
			APTR list = pImageList_Create(img->width, img->height,
				img->bpp <= 24 ? ILC_COLORDDB : ILC_COLOR32, 1, 1);
			pImageList_Add(list, img->hbitmap, NULL);
			button->sb_Icon = il.himl = list;
			il.uAlign = button->sb_IconOrient;
			(&il.margin.left)[dir[il.uAlign]]   = (int16_t) (button->sb_IconMargin >> 16);
			(&il.margin.left)[dir[il.uAlign+5]] = (int16_t) (button->sb_IconMargin & 0xffff);
			button->sb_IconSize.cx = img->width  + il.margin.left + il.margin.right;
			button->sb_IconSize.cy = img->height + il.margin.top  + il.margin.bottom;
			if (dtor) GFX_FreeImage(img);
			SendMessage(w->sw_Handle, BCM_SETIMAGELIST, 0, (LPARAM) &il);
		}
	}
	w->sw_PostProcess = NULL;
	return 0;
}

/* Set custom button attributes */
static int SIT_SetButtonValues(SIT_Widget w, APTR call_data, APTR user_data)
{
	TagList *      tag   = call_data;
	SIT_Variant *  value = user_data;
	SIT_PushButton pb    = (SIT_PushButton) w;

	switch (tag->tl_TagID) {
	case SIT_CurValue:
		pb->sb_CurValue = value->pointer;
		if (value->pointer == NULL) return 1;
		w->sw_Flags |= SITF_Style3Changed;
		break;
	case SIT_CheckState:
		pb->sb_State = value->integer;
		w->sw_Flags |= SITF_Style1Changed;
		break;
	case SIT_IconMargin:
	case SIT_IconOrient:
	case SIT_IconPath:
	case SIT_IconImage:
		w->sw_Flags |= SITF_Style2Changed;
		SIT_SetWidgetValue(w, call_data, user_data);
		break;
	default:
		return SIT_SetWidgetValue(w, call_data, user_data);
	}

	w->sw_PostProcess = SIT_SetPushButtonStyles;
	return 0;
}

#undef SITF_Style3Changed

static int SIT_FinalizeButton(SIT_Widget w, APTR call_data, APTR user_data)
{
	SIT_PushButton button = (SIT_PushButton) w;
	if (button->sb_Icon) pImageList_Destroy(button->sb_Icon);
	if (button->sb_IconPath) free(button->sb_IconPath);
	return 0;
}

/* We only want to get mouse message */
static LRESULT CALLBACK SIT_ButtonSubClassHandler(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	SIT_Widget w = (SIT_Widget) GetProp(wnd, sit_class);

	if (w == NULL) return DefWindowProc(wnd, msg, wParam, lParam);

	switch (msg) {
	case WM_LBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_LBUTTONDBLCLK:
	case WM_RBUTTONDOWN:
	case WM_RBUTTONUP:
	case WM_RBUTTONDBLCLK:
	case WM_MBUTTONDOWN:
	case WM_MBUTTONUP:
	case WM_MBUTTONDBLCLK:
	case WM_MOUSEMOVE:
		if (HAS_1OF2EVT(w, SITE_OnMouseMove, SITE_OnClick))
			SIT_SubClassHandler(wnd, msg, wParam, lParam);
	}
	return CallWindowProc(w->sw_SuperProc, wnd, msg, wParam, lParam);
}


/* Create and initialize a button, check box or radio button */
BOOL SIT_CreateButton(SIT_Widget w, va_list args)
{
	SIT_PushButton button = (SIT_PushButton) w;
	HWND           hwnd;
	int            style = WS_CHILD;
	int            id = 0;

	/* Default values */
	w->sw_OptimalWidth = SIT_MeasureButton;
	w->sw_SetValue     = SIT_SetButtonValues;
	w->sw_Finalize     = SIT_FinalizeButton;
	button->sb_RadioID = 0xdeadbeef;

	SIT_ParseTags(w, args, w->sw_Class = ButtonClass);

	switch (button->sb_Type) {
	case SITV_CancelButton:  id = IDCANCEL; break;
	case SITV_DefaultButton: style |= BS_DEFPUSHBUTTON; id = IDOK; break;
	case SITV_3StateCB:      style |= BS_3STATE | BS_AUTO3STATE; // no break;
	case SITV_CheckBox:      style |= BS_CHECKBOX | BS_AUTOCHECKBOX; break;
	case SITV_ToggleButton:  style |= BS_CHECKBOX | BS_AUTOCHECKBOX | BS_PUSHLIKE; break;
	case SITV_RadioButton:   style |= BS_AUTORADIOBUTTON;
		/* Assign a RadioID automatically: not perfect if TabOrder is set, but better than nothing */
		if (button->sb_RadioID == 0xdeadbeef)
		{
			SIT_Widget prev;
			int        id;
			for (prev = TAIL(w->sw_Parent->sw_Children), id = 0; prev; PREV(prev))
			{
				#define	pb    ((SIT_PushButton)prev)
				if (prev->sw_Type == SIT_PUSHBUTTON && pb->sb_Type == SITV_RadioButton &&
				    pb->sb_Group == button->sb_Group) id ++;
				#undef pb
			}
			button->sb_RadioID = id;
			if (w->sw_Title) /* Do not use this trick when name is used as a shortcut for title */
			{
				/* Quick'n dirty hack: useful in serialized form, reduce declaration */
				LPWSTR num = wcschr(w->sw_Name, ':');
				if (num) button->sb_RadioID = wcstol(num+1, NULL, 10), *num = 0;
			}
		}
	}
	switch (button->sb_AlignText) {
	case SITV_AlignRight:  style |= BS_LEFTTEXT | BS_RIGHT; break;
	case SITV_AlignCenter: style |= BS_CENTER;
	}
	if (id == 0) id = MAKE_ID(w);

	if (IsVisible(w)) style |= WS_VISIBLE;
	if (HAS_TAB(w))
	{
		if (button->sb_Type == SITV_RadioButton)
		{
			/* Only the selected radiobutton should have WS_TABSTOP style */
			if (button->sb_State) style |= WS_TABSTOP;
		}
		else style |= WS_GROUP | WS_TABSTOP;
	}

	hwnd = w->sw_Handle = CreateWindow(WC_BUTTON, w->sw_Name, style,
		0, 0, 0, 0, SIT_GetContainer(w), (HMENU) id, instance, NULL);

	if (hwnd == NULL) return FALSE;
	SIT_SetDefaultFont(w);

	w->sw_SuperProc = (APTR) GetWindowLong(w->sw_Handle, GWL_WNDPROC);
	SetWindowLong(w->sw_Handle, GWL_WNDPROC, (LONG) SIT_ButtonSubClassHandler);

	if (w->sw_Title) SIT_SetTitle(w);

	/* Keep track of class inside callback */
	SetProp(hwnd, sit_class, (HANDLE) w);

	if (w->sw_PostProcess)
		w->sw_PostProcess(w, NULL, NULL);

	return TRUE;
}
